/// ================= 1.Promise对象的创建 ============================
// 术语决议（resolve）、 完成（fulfill）和拒绝（reject）
let _ = require("lodash");
let fnNum = 10;
if(!_.isEmpty(process.argv[2])) { fnNum = _.toNumber(process.argv[2]); }

function doFoo(x:number) {
    console.log('Waiting for ...',x);
    return new Promise((resolve,reject)=>{
        // have an Exception
        if(x>50) {
            console.log("Exception for ....",x*100);
            // 异常的发生会被Promise作为拒绝决议(reject)
            throw {
                code : 40001,
                message : "Param x is too big.."
            };
        }

        setTimeout(function(){
            if(x>20 && x<= 25) {
                // 如果你没有用任何值显式决议，那么这个值就是 undefined
                console.log("Note Resolve|Reject For ... ",x);
                // reject(x*100);
                return;
            }else if(x>25) {
                console.log("Reject For ....",x);
                // Promise定义方式使得只能被决议一次，如果调用resolve/reject多次，虽然代码块会执行到，但是Promise对象只接受第一定决议。
                reject(x*100);
                resolve("Be Not Processed");
                return;
            }
            console.log('Ending Resolve for ...',x);
            resolve(x);
            return;
        },x*100);
    });
}
var doFooRes = doFoo(fnNum);

// ======================= 2.Promise决议的处理 ==========================================
// 对于处理Promise结果的函数: promise.then(fn1,fn2).catch(fn3);
// 1. 如果promise中resolve返回，表示已决议状态，则fn1被调用。
// 2. 如果promise中reject返回，表示出错或拒绝状态，如果fn2定义的话，则fn2被调用; 否则fn3被调用。
// 3. 如果promise中throw exception，表示出错或拒绝状态， 如果fn2定义的话，则fn2被调用; 否则fn3被调用。
// 4. 如果promise没有resolve/reject/throw， 则fn1,fn2,fn3都不会被调用，即此promise状态是未决的。
// 最后，then的调用返回了另外一个Promise对象。
let processFooRes = doFooRes.then(function(waitingSeconds:number){
    console.log('==========doFooRes Resolve Function For ...',waitingSeconds);
    // 用这个值作为这个Promise对象的决议
    return "Step 2 : Return For ..."+waitingSeconds;
},function(rejectNum){
    console.log("==========DoFooRes Reject Function For ...",rejectNum);
}).catch((exceptionObject)=>{
    console.log("==========Exception Function For ...",exceptionObject);
});



// ==================== 3.Promise 链式调用 ============================================
// .then函数的返回值（processFooRes变量)也是一个Promise对象，可以继续.then调用（其决议的值是通过上一个决议函数的return返回值 （注意是return,而不是resolve了) 提供的)。
// 当然，作为Promise对象来说，.then.then...可以作为链式处理一直链接下去。
processFooRes.then(function(returnForStep1:string){
    console.log(returnForStep1);  // "Step 2 : Return For ... 43"
});



// =================== 4.Promise结果还可以被多次观察 ===================================
// Promise一旦被决议，其决议的数据就被固定，不会变更了，所以可以拿过来多次观察。
// function watchFooRes(promiseResult:Promise<Object>){
//     promiseResult.then(function(waitSeconds:number){
//         console.log('============Watch doFooRes result========',waitSeconds);
//     });
// }
// watchFooRes(doFooRes);
// watchFooRes(doFooRes);




// ================== 5.Promise的resolve并不是一定返回一个立即值(结果或对象)，还可以是另外一个Promise对象 ===
let promiseObject3 = new Promise((resolve,reject) => { resolve("PromiseObject3"); });
// 以下resolve另一个Promise的对象.
let promiseObject2 = new Promise((resolve,reject) => { resolve(promiseObject3); });
let promiseObject1 = new Promise((resolve,reject) => { resolve("PromiseObject1"); });

// 注意下面的时序问题，次序必须是 "PromiseObject1" , "PromiseObject3"
promiseObject2.then(function(v){ console.log(v); })
promiseObject1.then(function(v){ console.log(v); })


// ================== 6.Promise的另外一种简易用法 =======================
let stylePromise1 = new Promise(function(resolve,reject){
    resolve(42);
});
// 等同于====>
let stylePromise2 = Promise.resolve(42);
// 可以在resolve中决议另一个Promise对象
let stylePromise3 = Promise.resolve(stylePromise2);



//// =================== 7. Promise链式处理中的异步决议 =======================
// 我们知道在Promise的链式处理中，上一个Promise的then返回值会作为下一个链式then内的决议.
// 但是有一个场景是想在then中使用异步，并且将异步处理的结果返回怎么办？
Promise.resolve(43).then(function(num:number){
    return num+1;        // 对于立即值，使用return就可以了，这里44作为下一个then的决议
    // 但是如果想要异步返回数据的决议==> return ajax(....); 肯定是不行的。
}).then(function(num:number){
    console.log(num);    // 值:44
});

// =====> then内的异步的决议
// 这种强大实在不可思议！现在我们可以构建这样一个序列：不管我们想要多少个异步步骤，每一步都能够根据需要等待下一步
Promise.resolve(43).then(function(num:number){
    // 如果想要异步返回数据的决议,则返回new Promise对象
    return new Promise(function(resolve,reject){
        setTimeout( ()=>{ resolve(++num); },2000);
    });
}).then(function(num:number){
    console.log("After Async Then....",num);
});


//// ==================8. 链式处理的示例 ====================================
let getUserWxLogin = function(appId:string) { return Promise.resolve({openId:"11111"}); };
let getUserInfo  = function(openId:string) { return Promise.resolve({userId:"shipfi"}); };
let getUserRole = function(userId:string) {return Promise.resolve({role:"manager"}); };
let getUserManagerSites = function(userId:string,role:string) {return Promise.resolve({sites:[1,2,3]}); };


// 有了上面方式，就应该抛弃掉以前写的那种回调地狱般的代码
// **** 一定要注意其中三个示例的差别，理解Promise所返回决议。 ***
function getAllUserInfo1(appId:string) {

    // 注1 : 一般的函数返回userInfo决议内容
    // 在这个函数里，注意不能直接返回 return getUserWxLogin.then(...), 因为其只是getUserWxLogin的决议，而不是整体四个函数调用完成后的决议，
    // 在getUserWxLogin函数中，只是调用了另一个Promise方法(任务事件)，并且没有return任何内容，所以返回值是undefined值
    // return new Promise(function(resolve,reject){
    //     getUserWxLogin(appId).then(function({openId}) {
    //         getUserInfo(openId).then(function({userId}) {
    //             getUserRole(userId).then(function({role}) {
    //                 getUserManagerSites(userId,role).then(function({sites}) {
    //                     resolve({
    //                         openId,
    //                         userId,
    //                         role,
    //                         sites
    //                     });
    //                 })
    //             })
    //         })
    //     });
    // });

    // 注2 : 测试用，如果不包装new Promise,但又要返回内容，怎么办？
    // 以下调用了getUserWxLogin.then(),在其函数中，调用了另一个Promise方法，且返回了决议内容(openId)
    // return getUserWxLogin(appId).then(function({openId}) {
    //     // 异步调用其它函数...不属于此函数决议内容了
    //     getUserInfo(openId).then(function({userId}) {
    //         getUserRole(userId).then(function({role}) {
    //             getUserManagerSites(userId,role).then(function({sites}) {
    //                 return {
    //                     openId,
    //                     userId,
    //                     role,
    //                     sites
    //                 }; // 注意函数的决议返回不到这儿....
    //             })
    //         })
    //     });
    //     return openId;   // 此函数返回决议内容
    // });


    // 注3 : 如果不写new Promise,是不是还有别的办法，返回想要返回的结构内容？？？
    // 那就是一层层返回下去，直到返回真正的userInfo决议内容
    return getUserWxLogin(appId).then(function({openId}) {
        return getUserInfo(openId).then(function({userId}) {
            return getUserRole(userId).then(function({role}) {
                return getUserManagerSites(userId,role).then(function({sites}) {
                    return {
                        openId,
                        userId,
                        role,
                        sites
                    };
                })
            })
        })
    });
}

getAllUserInfo1("myAppId").then(function(userInfo){
    console.log(userInfo);
});


// 逻辑更清晰的版本，再也不用一层层返回决议
// 注意： Promise 链不仅是一个表达多步异步序列的流程控制，还是一个从一个步骤到下一个步骤传递消息的消息通道
function getAllUserInfo2(appId:string) {
    let userInfo : any = new Object();
    return getUserWxLogin(appId).then(function({openId}){
        console.log(openId);
        userInfo['openId'] = openId;
        //  相当于
        // return new Promise(function(resolve,reject){ resolve(getUserInfo(openId)); });
        // return 1;

        return Promise.resolve(getUserInfo(openId));
    }).then(function({userId}){
        userInfo['userId'] = userId;
        return Promise.resolve(getUserRole(userId));
    }).then(function({role}){
        userInfo['roleId'] = role;
        return Promise.resolve(getUserManagerSites(userInfo['userId'],role));
    }).then(function({sites}){
        userInfo['sites'] = sites;
        return userInfo;
    }).catch(function(err){

    });
}
getAllUserInfo2("myAppId").then(function(userInfo){
    console.log(userInfo);
});




//// ================== 9. Promise不产生任何决议数据 ====================================
// 注意，是不产生决议数据，而不是决议不返回，这两者是有相当大的区别的。
function delay(time:number) {
    return new Promise(function(resolve,reject){
        // 直接使用resolve.resolve只是一个function的引用
        setTimeout(resolve,time);
    })
}
// 注意，delay函数的决议没有内容，所以接收的函数也没有参数
delay(100).then(function(){
    console.log("Step 2 (after 100ms)");
    // 在处理Promise决议的过程中，可以返回立即值,也可以返回一个Promise对象...
    return delay(200);
}).then(function(){
    console.log("Step 3 (after another 200ms)");
    return delay(50);
}).then(function (){
    console.log("Step 4 (Finish job)");
});


//// ================ 10. Promise的错误处理 =====================================
// 错误和异常是基于每个 Promise 的，这意味着可能在链的任意位置捕捉到这样的错误
// 并且，很重要的一点: 如果在then处理决议时没有提供拒绝的函数，则会产生一个默认的拒绝函数，而默认拒绝函数的工作就是将错误抛出。
// 基于以上的处理，在下一个决议处理函数中，可以继续捕获进行处理。这样，整个链又回到正常状态。
//  Promise ==> .then( throw err ) ===> .then(不提供拒绝函数的处理) ===> .then(可提供拒绝函数处理err异常)
//  *** 错误异常可以跨链传递 ***
Promise.resolve("Doing Sth Job and Return")
    // 第一个链中，产生错误异常
    .then(function(data){
        throw {
            "message" : "Some error in :"+data
        }
    })
    // 第二个链中，没有提供处理拒绝的函数，所以默认将错误抛出
    .then(function() {
       // 正常处理函数，因上个链处理在处理决议中产生异常，此代码不会被执行
       console.log("Never run here 1.");
    })
    // 在第三个链中，只是处理了第二个链抛过来的错误异常,并返回决议数据
    .then(data=>{console.log("Never run here 2.")})
    // 在第四个链中，处理正常的决议数据，这里打印"Now Error Fixed"
    .then(data=>{ console.log(data); })
    .catch(function(err){/**/});

